// 使用Boost::Asio库, 实现Http的下载
// 使用Boost::Asio库, 不但可以实现下载, 

#include "stdafx.h"

#include <boost/asio.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/algorithm/string.hpp>

#include <fstream>

using boost::asio::ip::tcp;

struct HttpResponse
{
public:
	explicit HttpResponse(){ clear();}
	std::string http_version;		// 版本
	unsigned int status_code;		// 状态码
	std::string status_message;	// 状态
	std::string header;					// HTTP包头
	std::string body;           // HTTP返回的内容
	std::string content_type;
	std::string modify_time;
	unsigned int content_length;
	unsigned int total_length;
	unsigned int offset;

	void clear()
	{
		http_version.clear();
		status_code = -1;
		status_message.clear();
		header.clear();
		content_type.clear();
		modify_time.clear();
		content_length = 0;
		total_length = 0;
		offset = 0;
		body.clear();
	}
};


struct DownloadInfo
{
	DownloadInfo()
	{
		id = 0;
		url.clear();
		filename.clear();
		md5.clear();
		writesize = 0;
		continued = false;
		lasterr = 0;
		trycount = 0;
	}
	int id;
	std::string url;
	std::string filename;
	std::string md5;
	int writesize;
	bool continued;
	int lasterr;
	int trycount;
};

// 获取文件
bool GetHttpFile(const std::string& szHost, const std::string& szParam)
{
	DownloadInfo* d_diCurrent = new DownloadInfo();
	d_diCurrent->filename = "DoLang.wma";

	try
	{
		std::ofstream f_ofsSave;

		boost::asio::io_service io_serv;

		// Get a list of endpoints corresponding to the server name.
		std::string szService ("http");
		std::string szIp = szHost;
		int i = szHost.find(":") ;
		if (i != -1)
		{
			szService = szHost.substr(i+1);
			szIp = szHost.substr(0, i);
		}
		tcp::resolver::query query(szIp, szService);

		tcp::resolver m_resolver(io_serv);
		tcp::socket s_socket(io_serv); 

		tcp::resolver::iterator endpoint_iterator = m_resolver.resolve(query), end_it;

		// Try each endpoint until we successfully establish a connection.
		tcp::resolver::iterator it = boost::asio::connect(s_socket, endpoint_iterator);

		if(it == end_it)
			return false;

		boost::asio::streambuf request;
		{
			std::ostream request_stream(&request);		

			request_stream << "GET " ;
			request_stream << szParam << " HTTP/1.1\r\n";
			request_stream << "Host: " << szHost << "\r\n";

			request_stream << "Accept: */*\r\n";
			request_stream << "Pragma: no-cache\r\n";
			request_stream << "Cache-Control: no-cache\r\n";
			request_stream << "Connection: close\r\n";

			request_stream << "\r\n";

			boost::asio::write(s_socket, request);
		} 

		// ************************************************************** 
		// 取出数据
		// ************************************************************** 
		boost::asio::streambuf response;
		std::ostringstream packetStream;
		try
		{
			// Read until EOF, writing data to output as we go.
			bool hasReadHeader = false;

			boost::system::error_code error;

			HttpResponse result;
			result.body.clear();

			while (boost::asio::read(s_socket, response,
				boost::asio::transfer_at_least(1), error))
			{
				packetStream.str("");

				packetStream << &response;

				std::string packetString = packetStream.str(); 

				if (!hasReadHeader)
				{
					// 取出http header
					size_t nEndHeader = packetString.find("\r\n\r\n");
					if(nEndHeader == std::string::npos)
						continue;

					hasReadHeader = true;
					result.header = packetString.substr(0, nEndHeader);

					// *****************************************************
					// 分析文件头
					// *****************************************************
					// Check that response is OK.
					std::istringstream response_stream(result.header);
					response_stream >> result.http_version;
					response_stream >> result.status_code;
				
					std::string strLine;
					std::getline(response_stream, strLine);
					while (!strLine.empty())
					{
						if (strLine.find("Content-Type:") != std::string::npos)
						{
							result.content_type = strLine.substr(strlen("Content-Type:"));
							result.content_type.erase(0, result.content_type.find_first_not_of(" "));
						}
						if (strLine.find("Content-Length:") != std::string::npos)
						{
							result.content_length = atoi(strLine.substr(strlen("Content-Length:")).c_str());
							result.total_length = result.content_length;
						}
						if (strLine.find("Last-Modified:") != std::string::npos)
						{
							result.modify_time = strLine.substr(strlen("Last-Modified:"));
							result.modify_time.erase(0, result.modify_time.find_first_not_of(" "));
						}	
						if (strLine.find("Content-Range: bytes") != std::string::npos)
						{
							std::string tmp = strLine.substr(strlen("Content-Range: bytes"));
							result.offset = atoi(tmp.substr(0, tmp.find('-')).c_str());
							int ipos = tmp.find('/');
							int ivalue = 0;
							if (ipos != std::string::npos)
							{
								ivalue = atoi(tmp.substr(ipos+1).c_str());
							}
							if (ivalue) 
								result.total_length = ivalue;
						}
						strLine.clear();
						std::getline(response_stream, strLine);
					}

					if ( result.http_version.substr(0, 5) != "HTTP/")
					{
						std::cout << "Invalid response\n";
						return false;
					}
					if (result.status_code != 200 && result.status_code != 206)
					{
						std::cout << "Response returned with status code " 
							<< result.status_code << "\n";
						return false;
					}

					packetString.erase(0, nEndHeader + 4);
				}

				// **************************************************************
				// 写入文件
				// **************************************************************
				{
					if (!f_ofsSave.is_open())
					{
						std::string fn = d_diCurrent->filename;
						if (d_diCurrent->continued)
						{
							fn = d_diCurrent->filename+std::string(".td");
						}

						{
							f_ofsSave.open(fn, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc );
						}

						if (!f_ofsSave.is_open()) 
						{
							return false;
						}

						d_diCurrent->writesize = 0;
					}

					try {
						f_ofsSave.write(packetString.c_str(), packetString.length());
						d_diCurrent->writesize += packetString.length();
					}
					catch (std::exception &e)
					{
						return false;
					}
				}
			}

			// 关闭文件
			f_ofsSave.close();

			if (error != boost::asio::error::eof)
				throw boost::system::system_error(error);

		}
		catch (std::exception& e)
		{
			std::cout << "Exception: " << e.what() << "\n";
			return false;
		}
	}
	catch (std::exception& e)
	{
		std::cout << "Exception: " << e.what() << "\n";
		return false;
	}

	return true;
}

// 获取网址对应的IP地址
bool GetHttpIp()
{
	try
	{
		boost::asio::io_service io_service;
		tcp::resolver resolver(io_service);
		tcp::resolver::query query("www.csdn.net", "http");
		boost::system::error_code e;
		tcp::resolver::iterator iter = resolver.resolve(query, e);
		tcp::resolver::iterator end;
		while (iter != end)
		{
			tcp::endpoint ep = *iter++;
			std::cout<<ep<<std::endl;
		}
	}
	catch (std::exception& e)
	{
		std::cerr << e.what() << std::endl;
	}

	return true;
}

int _tmain(int argc, _TCHAR* argv[])
{
	std::string szHost ("192.168.1.101");
	std::string szParam("/DoDo/aishen.mp4");

	GetHttpFile(szHost, szParam);

	//GetHttpIp();

	system("pause");
	return 0;
}